import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  OnGatewayConnection,
  OnGatewayDisconnect,
  MessageBody,
  ConnectedSocket,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { Logger } from '@nestjs/common';
import { DatabaseService } from '../services/database.service';

interface TokenChatClientInfo {
  socketId: string;
  username?: string;
  tokenMint?: string;
  joinedAt?: Date;
}

interface TokenChatMessage {
  id: string;
  username: string;
  message: string;
  timestamp: string;
  tokenMint: string;
  isSystem?: boolean;
}

interface TokenChatUser {
  id: string;
  username: string;
  isOnline: boolean;
  joinedAt: string;
  tokenMint: string;
}

@WebSocketGateway({
  cors: {
    origin: (origin, callback) => {
      // 在开发环境中允许所有来源
      if (process.env.NODE_ENV === 'development') {
        callback(null, true);
        return;
      }

      // 在生产环境中允许特定的来源
      const allowedOrigins = [
        'http://localhost:7171',
        'http://104.129.59.158:7171',
        'https://c4pad.fun',
        'http://104.129.59.158',
        'https://104.129.59.158',
        process.env.WEBSOCKET_CORS_ORIGIN,
        process.env.FRONTEND_URL,
      ].filter(Boolean);

      if (!origin || allowedOrigins.includes(origin)) {
        callback(null, true);
      } else {
        callback(new Error('Not allowed by CORS'));
      }
    },
    credentials: true,
  },
  namespace: '/token-chat',
})
export class TokenChatGateway
  implements OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer()
  server: Server;

  private readonly logger = new Logger(TokenChatGateway.name);
  private clients = new Map<string, TokenChatClientInfo>();

  // 按代币存储聊天消息和用户
  private tokenMessages = new Map<string, TokenChatMessage[]>();
  private tokenUsers = new Map<string, Map<string, TokenChatUser>>();

  constructor(private readonly database: DatabaseService) {}

  async handleConnection(client: Socket) {
    const tokenMint = client.handshake.query.tokenMint as string;
    const username = client.handshake.auth?.username as string;

    this.logger.log(`Token chat connection: ${client.id}, token: ${tokenMint}`);

    const clientInfo: TokenChatClientInfo = {
      socketId: client.id,
      username,
      tokenMint,
      joinedAt: new Date(),
    };

    this.clients.set(client.id, clientInfo);

    // 保存连接到数据库 (简化版本)
    try {
      await this.database.webSocketConnection.create({
        data: {
          socketId: client.id,
          connectedAt: new Date(),
        },
      });
    } catch (error) {
      this.logger.error('Failed to save token chat connection to database:', error);
    }

    client.emit('connected', {
      message: `Connected to token chat for ${tokenMint}`,
      timestamp: new Date().toISOString(),
    });
  }

  async handleDisconnect(client: Socket) {
    const clientInfo = this.clients.get(client.id);
    if (clientInfo) {
      this.handleTokenChatLeave(client);
    }

    this.clients.delete(client.id);

    // 删除数据库连接记录
    try {
      await this.database.webSocketConnection.deleteMany({
        where: { socketId: client.id },
      });
    } catch (error) {
      this.logger.error('Failed to delete token chat connection from database:', error);
    }

    this.logger.log(`Token chat client disconnected: ${client.id}`);
  }

  @SubscribeMessage('joinTokenRoom')
  async handleJoinTokenRoom(
    @MessageBody() data: { tokenMint: string; username: string },
    @ConnectedSocket() client: Socket,
  ) {
    const { tokenMint, username } = data;
    this.logger.log(`尝试加入代币房间: ${tokenMint}, 用户名: ${username}, 客户端: ${client.id}`);
    
    const clientInfo = this.clients.get(client.id);

    if (!clientInfo) {
      this.logger.warn(`客户端信息未找到: ${client.id}`);
      return;
    }

    // 检查用户名是否在该代币房间中已被使用
    if (!this.tokenUsers.has(tokenMint)) {
      this.tokenUsers.set(tokenMint, new Map());
    }

    const tokenRoomUsers = this.tokenUsers.get(tokenMint)!;
    const isUsernameTaken = Array.from(tokenRoomUsers.values()).some(
      (user) => user.username === username && user.id !== client.id,
    );

    if (isUsernameTaken) {
      client.emit('username_taken', {
        message: `用户名 ${username} 在该代币房间中已被使用`,
      });
      return;
    }

    // 更新客户端信息
    clientInfo.username = username;
    clientInfo.tokenMint = tokenMint;

    // 加入代币特定的房间
    const roomName = `token-${tokenMint}`;
    client.join(roomName);
    this.logger.log(`客户端 ${client.id} 加入房间: ${roomName}`);

    // 创建用户对象
    const chatUser: TokenChatUser = {
      id: client.id,
      username,
      isOnline: true,
      joinedAt: new Date().toISOString(),
      tokenMint,
    };

    // 添加到代币房间用户列表
    tokenRoomUsers.set(client.id, chatUser);
    this.logger.log(`房间 ${roomName} 当前用户数: ${tokenRoomUsers.size}`);

    // 通知房间中的其他用户
    this.server.to(roomName).emit('user_joined', {
      user: chatUser,
      timestamp: new Date().toISOString(),
    });

    // 发送聊天历史
    const messages = this.tokenMessages.get(tokenMint) || [];
    client.emit('tokenChatHistory', messages.slice(-50)); // 只发送最近50条消息

    // 发送当前在线用户列表
    const usersList = Array.from(tokenRoomUsers.values());
    client.emit('online_users', usersList);
    this.logger.log(`发送在线用户列表给 ${client.id}: ${usersList.length} 个用户`);

    this.logger.log(`用户 ${username} 加入代币 ${tokenMint} 聊天室`);
  }

  @SubscribeMessage('send_message')
  async handleSendMessage(
    @MessageBody() data: { message: string; tokenMint: string },
    @ConnectedSocket() client: Socket,
  ) {
    const clientInfo = this.clients.get(client.id);
    if (!clientInfo || !clientInfo.username || !clientInfo.tokenMint) {
      return;
    }

    const { message, tokenMint } = data;
    if (!message.trim() || tokenMint !== clientInfo.tokenMint) {
      return;
    }

    // 创建消息对象
    const chatMessage: TokenChatMessage = {
      id: `msg-${Date.now()}-${client.id}`,
      username: clientInfo.username,
      message: message.trim(),
      timestamp: new Date().toISOString(),
      tokenMint,
    };

    // 存储消息（每个代币最多存储1000条）
    if (!this.tokenMessages.has(tokenMint)) {
      this.tokenMessages.set(tokenMint, []);
    }
    const messages = this.tokenMessages.get(tokenMint)!;
    messages.push(chatMessage);
    if (messages.length > 1000) {
      this.tokenMessages.set(tokenMint, messages.slice(-1000));
    }

    // 广播消息给该代币房间中的所有用户
    const roomName = `token-${tokenMint}`;
    this.server.to(roomName).emit('new_message', chatMessage);

    this.logger.log(`代币 ${tokenMint} 聊天消息: ${clientInfo.username}: ${message.trim()}`);
  }

  @SubscribeMessage('change_username')
  async handleChangeUsername(
    @MessageBody() data: { newUsername: string },
    @ConnectedSocket() client: Socket,
  ) {
    const clientInfo = this.clients.get(client.id);
    if (!clientInfo || !clientInfo.tokenMint) {
      return;
    }

    const { newUsername } = data;
    const oldUsername = clientInfo.username;
    const tokenMint = clientInfo.tokenMint;

    if (!newUsername.trim() || newUsername === oldUsername) {
      return;
    }

    // 检查新用户名是否在该代币房间中已被使用
    const tokenRoomUsers = this.tokenUsers.get(tokenMint);
    if (tokenRoomUsers) {
      const isUsernameTaken = Array.from(tokenRoomUsers.values()).some(
        (user) => user.username === newUsername.trim() && user.id !== client.id,
      );

      if (isUsernameTaken) {
        client.emit('username_taken', {
          message: `用户名 ${newUsername} 在该代币房间中已被使用`,
        });
        return;
      }

      // 更新用户信息
      const user = tokenRoomUsers.get(client.id);
      if (user) {
        user.username = newUsername.trim();
        clientInfo.username = newUsername.trim();

        // 通知房间中的其他用户
        const roomName = `token-${tokenMint}`;
        this.server.to(roomName).emit('username_changed', {
          oldUsername,
          newUsername: newUsername.trim(),
          userId: client.id,
          timestamp: new Date().toISOString(),
        });

        this.logger.log(`代币 ${tokenMint} 房间用户 ${oldUsername} 更改用户名为 ${newUsername.trim()}`);
      }
    }
  }

  @SubscribeMessage('leave_chat')
  async handleLeaveChat(@ConnectedSocket() client: Socket) {
    this.handleTokenChatLeave(client);
  }

  private handleTokenChatLeave(client: Socket) {
    const clientInfo = this.clients.get(client.id);
    if (!clientInfo || !clientInfo.tokenMint) {
      return;
    }

    const tokenMint = clientInfo.tokenMint;
    const roomName = `token-${tokenMint}`;
    const tokenRoomUsers = this.tokenUsers.get(tokenMint);

    if (tokenRoomUsers) {
      const user = tokenRoomUsers.get(client.id);
      if (user) {
        tokenRoomUsers.delete(client.id);

        // 通知房间中的其他用户
        this.server.to(roomName).emit('user_left', {
          userId: client.id,
          timestamp: new Date().toISOString(),
        });

        this.logger.log(`用户 ${user.username} 离开代币 ${tokenMint} 聊天室`);
      }
    }

    client.leave(roomName);
  }
}
